<template>
  <view class="appliance-control" :class="{ 'is-sticky': isSticky }">
    <block v-if="showControlView || showControlViewForced || showSkeleton || showOfflineTips">
      <ApplianceLayout
        v-model:is-sticky="isSticky"
        ref="layoutRef"
        :flex="showSkeleton || showOfflineTips"
        :sticky="usingSticky"
        :no-action-bar="noActionBar || showOfflineTips || showPowerOffTips"
        :no-overview-suffix="!hasOverviewSuffix"
        :show-disabled-mask="isOffline === true"
        @sticky-change="onStickyChange"
      >
        <template #header>
          <HeaderBar
            v-if="!showSkeleton"
            :control="control"
            :check-update="checkUpdate"
            :share-disabled="shareDisabled"
          />
        </template>

        <template #cover>
          <view v-if="showSkeleton" class="skeleton-item cover-skeleton"></view>
          <block v-else>
            <CoverPicture :src="coverImage || control.store.coverImage" :disabled="isOffline === true" />
            <slot name="cover-suffix"></slot>
          </block>
        </template>

        <template #cover-aside>
          <block v-if="!showSkeleton">
            <slot name="aside-control"></slot>
          </block>
        </template>

        <template #sticky-prefix>
          <block v-if="showLayoutNormally">
            <view v-if="flexAreaLinks?.length" class="flex-area">
              <FlexArea
                class="flex-area"
                ref="flexAreaRef"
                :links="flexAreaLinks"
                @item-click="emit('flex-area-click', $event)"
              />
            </view>

            <!-- <view v-if="recentPrograms?.length" class="recent-cards">
              <RecentCards use-plain-disabled :control="control" :type="HistoryProgramTypes.StartedProgram" />
            </view> -->
          </block>
        </template>

        <view class="control-main" :class="{ 'offline-tips': showOfflineTips, 'power-off-tips': showPowerOffTips }">
          <template v-if="showSkeleton">
            <view class="skeleton-item program-tabs-skeleton"></view>
            <view class="skeleton-grid program-list-skeleton">
              <view v-for="count in 8" :key="count" class="skeleton-item program-item-skeleton"></view>
            </view>
          </template>

          <template v-else-if="showOfflineTips">
            <text>{{ $t('Appliance.OfflineState.Tips.Text') }}</text>
          </template>
          <template v-else-if="showPowerOffTips">
            <text>{{ $t('Appliance.PowerOffState.Tips.Text') }}</text>
          </template>

          <template v-else>
            <ProgramList
              :tab-bar-sticky="isSticky"
              :active-tab-extra="favoriteGroupSelected"
              :disabled="isOffline === true"
              :plain-disabled="programSelectDisabled"
              :programs="!showPowerOffTips ? programs : []"
              :active-key="activeProgramKey"
              :active-tips="activeProgramTips || activeProgramRemaining"
              :groups="programGroups"
              :active-group-key="currentProgramGroupKey"
              :is-show-local="isShowLocal"
              :custom-name-getter="customProgramNameGetter"
              @group-select="onProgramGroupSelect"
              @select="onProgramSelect"
              @add-favorite="openAddFavorite"
            >
              <template #tabs-extra>
                <slot name="tabs-extra"></slot>
                <TextButton
                  v-if="isShowFavoriteManageBtn"
                  class="favorite-manage"
                  size="small"
                  inline
                  :disabled="!isPowerOn"
                  @click="openFavoriteMange"
                >
                  <text>{{ $t('Appliance.FavoriteManage.Button.Text') }}</text>
                </TextButton>
              </template>
            </ProgramList>
          </template>
        </view>

        <template #action-bar>
          <view v-if="!noActionBar && !showOfflineTips && !showPowerOffTips" class="action-button-group">
            <template v-if="showSkeleton">
              <view class="skeleton-item action-button-skeleton"></view>
            </template>

            <template v-else-if="showLayoutNormally">
              <slot name="action-prefix"></slot>
              <view class="action-main">
                <block v-if="useCustomAction">
                  <slot name="custom-action"></slot>
                </block>
                <ActionGroup
                  v-else
                  :disabled="isOffline === true"
                  :plain-disabled="adjustParameterDisabled && startDisabled"
                >
                  <ActionButton
                    v-if="!isHideAdjustBtn"
                    style="flex-basis: 35%"
                    :text="customAdjustOptionTitle || $t('Appliance.AdjustOption.Button.Text')"
                    :disabled="isOffline === true"
                    :plain-disabled="adjustParameterDisabled"
                    @click="adjustParameter"
                  />
                  <ActionButton
                    vibrate
                    style="flex-basis: 65%"
                    :text="$t('Appliance.StartProgram.Button.Text')"
                    :disabled="isOffline === true"
                    :plain-disabled="startDisabled"
                    @click="startNow"
                  />
                </ActionGroup>
              </view>
            </template>
          </view>
        </template>
      </ApplianceLayout>
    </block>

    <StateView
      v-else
      :control="control"
      :custom-name-getter="customProgramNameGetter"
      :custom-state-getter="customStateGetter"
      @action="onStateAction"
      @adjust="adjustParameter"
    >
      <template #header-right>
        <HeaderBar
          v-if="!showSkeleton"
          :control="control"
          :check-update="checkUpdate"
          :share-disabled="shareDisabled"
        />
      </template>
      <template #state-view-prefix>
        <slot name="state-view-prefix"></slot>
      </template>
      <template #state-view-suffix>
        <slot name="state-view-suffix"></slot>
      </template>
    </StateView>

    <view class="root-modals">
      <NoticeBar :control="control" :external-methods="noticeMergedMethods" :prevent-action="preventNoticeAction" />
      <!-- 选择收藏 -->
      <SelectFavorite
        v-model:show="selectFavoriteVisible"
        :control="control"
        @update-favorite="openUpdateFavorite('addFavorite')"
      />
      <!-- 收藏管理列表 -->
      <FavoriteManage
        v-model:show="favoriteManageVisible"
        :control="control"
        @add-favorite="openAddFavorite"
        @update-favorite="openUpdateFavorite('updateFavorite')"
      />
      <!-- 更新收藏 -->
      <UpdateManage
        v-model:show="updateFavoriteVisible"
        :control="control"
        :from="fromSource"
        @add-favorite-success="selectFavoriteVisible = false"
      />
      <RemoteHint
        v-model:show="remoteHintVisible"
        :vib="control.store._datasource.vib"
        :title="customRemoteHintOptions?.title || remoteHintTitle"
        :dismiss-text="customRemoteHintOptions?.dismissText || remoteHintDismissText"
        :cancel-text="customRemoteHintOptions?.cancelText || ''"
        :steps-getter="getRemoteHintSteps"
        @confirm="onRemoteHintConfirm"
        @cancel="onRemoteHintCancel"
      />
      <Mask :show="control.store.isStartingUp" type="loading">
        <rich-text :nodes="$t('Appliance.PowerOn.Loading.Text')" />
      </Mask>
      <Mask :show="control.store.isShuttingDown" type="loading">
        <rich-text :nodes="$t('Appliance.PowerOff.Loading.Text')" />
      </Mask>
      <slot></slot>
    </view>
  </view>
</template>

<script setup lang="ts">
import type { ApplianceProgram } from '@/types/appliance'
import ApplianceLayout from '@/layout/appliance/index.vue'
import TextButton from '@/common/textButton.vue'
import ActionGroup from '@/common/actionButton/actionGroup.vue'
import ActionButton from '@/common/actionButton/actionButton.vue'
import HeaderBar from '../../components/headerBar.vue'
import NoticeBar, { type ShouldPreventAction } from '../notification/noticeBar.vue'
import SelectFavorite from '../../components/selectFavorite.vue'
import FavoriteManage from '../../components/favoriteManage.vue'
import UpdateManage from '../../components/updateFavorite.vue'
import Mask from '@/common/mask.vue'
import type { BaseController } from '@/controller/BaseAppliance/definition'
import StateView, { type CustomConfigGetter, type StateAction } from '../stateView.vue'
import ProgramList, { type CustomProgramNameGetter } from '../../components/programList.vue'
import CoverPicture from './components/CoverPicture.vue'
import FlexArea, { type FlexAreaLink } from './components/FlexArea.vue'
import { useControlState } from '@/controller/BaseAppliance/useControl'
// import RecentCards from './components/RecentCards.vue'
// import { HistoryProgramTypes } from '@/api/appliance'
import { TrackType, trackEvent } from '@/dataReport/tracks'
import type { NoticeLinkCallback, NoticeLinkPayload } from '../notification/useNoticeControl'
import type { InvokeMethodAction } from '@/types/notice'
import RemoteHint, { type StepsGetter } from '../remoteHint/remoteHint.vue'
import { OpenDrumCleanAvailable } from '@/constants/UniEventKeys'
import { actionDebounce, useUniEvent } from '@/utils/mpHelper'
import {
  checkAdjustAccessable,
  checkStartProgramAccessable,
  checkValleyTimeAccessable,
} from '@/controller/common/ErrorHandler/validator'
import { safeTrack } from '@/dataReport/dataReportUtil'
import { orderPermanentInfo } from '@/utils/deviceMessageSubscribe/deviceMessage'
import type { ChapterItem } from '@/api/content'

export type CustomRemoteHintOptions = {
  title: string
  dismissText: string
  cancelText?: string
  stepsGetter?: StepsGetter
  onConfirm: () => any
  onCancel: () => any
}

const props = defineProps<{
  control: BaseController.Control
  customStateGetter?: CustomConfigGetter
  customProgramNameGetter?: CustomProgramNameGetter // 自定义程序名称
  showControlViewForced?: boolean // 是否强制显示详情页视图(程序列表视图)
  enablePowerOffTips?: boolean // 是否启用关机提示
  coverImage?: string
  flexAreaLinks?: FlexAreaLink[]
  activeProgramTips?: string // 当前程序显示的程序提示文本
  noActionBar?: boolean
  useCustomAction?: boolean
  checkUpdate?: boolean
  isShowLocal?: boolean
  isHideAdjustBtn?: boolean
  shareDisabled?: boolean // 是否置灰分享按钮
  buttonId?: string
  // 触发高度计算的Trigger
  heightEffectTriggers?: (() => any)[]
  customAdjustOptionTitle?: string
  // 通知栏查看详情定义方法
  noticeExternalMethods?: Record<string, NoticeLinkCallback>
  // 通知栏是否阻止action执行
  preventNoticeAction?: ShouldPreventAction

  // 自定义远程提示框操作
  customRemoteHint?: (payload: NoticeLinkPayload) => CustomRemoteHintOptions | undefined
}>()

const emit = defineEmits<{
  (e: 'state-action', action: StateAction): void
  (e: 'flex-area-click', link: FlexAreaLink): void
  (e: 'program-select', program: ApplianceProgram.Scope): void
  (e: 'adjust'): void
  (e: 'start-now', program: ApplianceProgram.Scope): void
  (e: 'update-favorite'): void
}>()

const store = computed(() => props.control.store)
const layoutRef = ref<InstanceType<typeof ApplianceLayout> | null>(null)
const flexAreaRef = ref<InstanceType<typeof FlexArea> | null>(null)
function closeFlexAreaDropdown() {
  flexAreaRef.value?.closeMoreDropdown()
}

/**
 * 通知栏
 */
const noticeMergedMethods = {
  openRemoteHint,
  ...props.noticeExternalMethods,
}

/**
 * 远程控制弹框 (通知栏触发)
 */
const remoteHintVisible = ref(false)
const remoteHintTitle = ref('')
const remoteHintDismissText = ref('')
const customRemoteHintOptions = ref<CustomRemoteHintOptions | null>(null)
function openRemoteHint(payload: NoticeLinkPayload) {
  if (typeof props.customRemoteHint === 'function') {
    customRemoteHintOptions.value = props.customRemoteHint(payload) || null
  } else {
    const { notice } = payload
    remoteHintTitle.value = (notice.action as InvokeMethodAction).title || ''
    remoteHintDismissText.value = (notice.action as InvokeMethodAction).dismissText || ''
  }

  remoteHintVisible.value = true
}
function getRemoteHintSteps(steps: ChapterItem[]): ChapterItem[] {
  return customRemoteHintOptions.value?.stepsGetter?.(steps) || steps
}
function onRemoteHintConfirm() {
  customRemoteHintOptions.value?.onConfirm()
}
function onRemoteHintCancel() {
  customRemoteHintOptions.value?.onCancel()
}

/**
 * 状态
 */
const controlState = useControlState(props.control)
const { isOffline, isPowerOn, showControlView, programSelectDisabled, adjustParameterDisabled, startDisabled } =
  controlState
// 是否显示骨架屏
const showSkeleton = computed(() => !props.control.store._datasource.loaded)
// 是否显示离线提示
const showOfflineTips = computed(
  () => props.control.store._datasource.loaded && isOffline.value === true && noProgramExist.value
)
// 是否显示关机提示
const showPowerOffTips = computed(() => props.control.store._datasource.loaded && props.enablePowerOffTips)
// 非离线提示 非骨架屏
const showLayoutNormally = computed(() => !showSkeleton.value && !showOfflineTips.value)
// 是否使用sticky布局
const usingSticky = computed(() => showLayoutNormally.value)
// 是否正在吸顶
const isSticky = ref(false)
// 是否隐藏封面底部
const hasOverviewSuffix = computed(() => {
  return showLayoutNormally.value && (props.flexAreaLinks?.length || recentPrograms.value?.length)
})
function onStateAction(action: StateAction) {
  emit('state-action', action)
}

// 桶清洁弹窗点击立即启动 打开程序详情页
useUniEvent(OpenDrumCleanAvailable, () => emit('adjust'))

/**
 * 当前程序
 */
const noProgramExist = computed(() => props.control.store.allPrograms === null)
const programGroups = computed(() => props.control.store.programGroups)
const currentProgramGroupKey = computed(() => props.control.store.currentProgramGroupKey)
const favoriteGroupSelected = computed(() => currentProgramGroupKey.value === 'Program.Favorite')
const programs = computed(() => props.control.store.programs)
const recentPrograms = computed(() => props.control.store.recentStartedPrograms)
const activeProgramKey = computed(() => props.control.store.currentProgramKey)
const activeProgramRemaining = computed(() => props.control.store.programRemainingText)
const favoriteProgramsList = computed(() => props.control.store.favoriteProgramsList || [])

/**
 * 选择程序
 */
async function onProgramGroupSelect(group: ApplianceProgram.Group) {
  store.value.currentProgramGroupKey = group.key
}
async function onProgramSelect(program: ApplianceProgram.Scope) {
  await store.value.setCurrentProgram(program)
  emit('program-select', program)
}

/**
 * 调节参数
 */
const adjustParameter = actionDebounce({
  delay: 3000,
  async call() {
    // 调接口前检查
    const accessable = checkAdjustAccessable(props.control)
    if (!accessable) return

    safeTrack(() => {
      trackEvent('Appliance.AdjustOption.Button.Text', {}, TrackType.ApplianceDetail)
    })

    emit('adjust')
  },
})

/**
 * 立即启动
 */
async function startNow() {
  safeTrack(() => {
    trackEvent('Appliance.StartProgram.Button.Text', {}, TrackType.ApplianceDetail)
  })
  // 调接口前检查
  if (!checkStartProgramAccessable(props.control)) return

  // 谷电时间检查
  const valleyTimeAccessable = await checkValleyTimeAccessable(props.control, {
    onAccessable: async () => {
      // 永久消息订阅
      try {
        await orderPermanentInfo(props.control.store._datasource.haId)
      } catch (e) {
        console.error(e)
      }
    },
  })
  if (!valleyTimeAccessable) return

  const currentProgram = store.value.currentProgram as ApplianceProgram.Scope
  currentProgram.trackName =
    props.customProgramNameGetter?.(currentProgram.key, currentProgram.name, true) || currentProgram.name

  await store.value.startProgramNow(currentProgram, null, 'Appliance.StartProgram.Button.Text')
}

/**
 * 吸顶布局相关
 */
function onStickyChange() {
  // 滚动时关闭下拉菜单
  closeFlexAreaDropdown()
}

// 触发高度计算
const preHeightEffectTriggers = [
  // 会影响首屏高度的数据trigger
  ...(props.heightEffectTriggers || []),
  () => recentPrograms.value,
]
preHeightEffectTriggers.forEach((trigger) => {
  watch(trigger, () => {
    layoutRef.value?.recalculatePreHeight()
  })
})

/**
 * 收藏
 */
const selectFavoriteVisible = ref(false)
const favoriteManageVisible = ref(false)
const fromSource = ref('')
const isShowFavoriteManageBtn = computed(
  () => currentProgramGroupKey.value === 'Program.Favorite' && favoriteProgramsList.value.length
)
// 打开添加收藏页面
function openAddFavorite() {
  selectFavoriteVisible.value = true
}
// 打开收藏管理页面
function openFavoriteMange() {
  favoriteManageVisible.value = true
}
// 打开更新收藏页面
const updateFavoriteVisible = ref(false)
function openUpdateFavorite(type: string) {
  fromSource.value = type
  updateFavoriteVisible.value = true
}
</script>

<style scoped lang="less">
@import '@/styles/skeleton.less';
@import './control.less';

.appliance-control {
  &.is-sticky .control-main {
    background-color: #ffffff;
  }
  .control-main {
    height: 100%;
  }
  .action-main {
    flex: 1;
  }
}
.root-modals {
  z-index: 3;
}
</style>
